import os
import pytest
import re
from common.Load_Yaml_Util.load_yaml import load_test_cases
from common.Load_Yaml_Util.load_yaml import load_config_host
from common.requests_util import RequestUtil
from common.logger_util import LogUtils
from common.response import ResponseObject
from common.assertions import AssertionMap

# 加载全局配置文件
# config = load_config_host('../configs/config.yaml')
config = load_config_host('configs/config.yaml')

# 初始化token变量为None
token = None

# 使用环境变量获取测试用例文件名，如果未设置，则默认为 'data/test_cases.yaml'
# test_cases_file = os.getenv('TEST_CASES_FILE', '../data/ctrler_yaml/UAT/test_path_create_del_d.yaml')
test_cases_file = os.getenv('TEST_CASES_FILE', 'data/test_logic_port.yaml')

# 字符串${} 包含的字符串
variable_pattern = re.compile(r'^\$\{(.*?)\}$')
def load_variables(variables,shared_data):
    """
    函数用于将变量加载到共享数据字典中
    Args:
        variables:
        shared_data:
    """
    for k in variables:
        shared_data[k] = variables[k]

def update_params(params, shared_data:dict):
    """替换params中的变量
        如果某个值符合variable_pattern正则  则替换
    Args:
        params (dict | list): 变更的字典或者数组
        shared_data (dict): 共享变量
    """
    if isinstance(params, dict):
        for k in params:
            if isinstance(params[k], dict) or isinstance(params[k], list):
                update_params(params[k], shared_data)
            elif isinstance(params[k],str):
                r = variable_pattern.search(params[k])
                if r:
                    key = r.group(1)
                    if key in shared_data:
                        params[k] = shared_data[key]
    elif isinstance(params, list):
        for idx in range(len(params)):
            if isinstance(params[idx], dict) or isinstance(params[idx], list):
                update_params(params[idx], shared_data)
            elif isinstance(params[idx], str):
                r = variable_pattern.search(params[idx])
                if r:
                    key = r.group(1)
                    if key in shared_data:
                        params[idx] = shared_data[key]

def update_items(items:list, shared_data:dict, resp_obj:ResponseObject):
    """替换items中的变量
        对items里面的字符串变量进行更换
        如果满足${}正则 就去全局共享变量里查找
        否则去resp_obj里找
    Args:
        items (list): 需要变换的数组
        shared_data (dict): 共享数据字典
        resp_obj (ResponseObject): 返回的结构体
    """
    for idx in range(len(items)):
        if isinstance(items[idx], str):
            r = variable_pattern.search(items[idx])
            if r:
                key = r.group(1)
                if key in shared_data:
                    items[idx] = shared_data[key]
            elif idx == 0:
                # 只对第一个变量做替换查找返回结果中的值
                # 这样就规定yaml中 -eq :[a, b] 此种格式 第一个数据可以是返回结果里面的值
                items[idx] = resp_obj._search_jmespath(items[idx])

# 加载TestSuit和测试用例
testsuit, test_cases = load_test_cases(test_cases_file)

class TestClass:
    # 共享变量
    shared_data = None
    
    @classmethod
    def setup_class(cls):
        # 初始化共享变量
        cls.shared_data = {}
        LogUtils.info(f"TestSuit: {testsuit}")
        
    @classmethod
    def teardown_class(cls):
        cls.shared_data = None

    # 参数化执行测试用例
    @pytest.mark.parametrize('test_case', test_cases)
    # @pytest.mark.parametrize('test_case', load_test_cases(test_cases_file))
    def test_run_test_case(self, test_case):
        global token  # 声明token为全局变量

        LogUtils.info('==============> 测试用例执行开始 <==============')
        try:
            # 构建完整的URL
            url = 'http://' + config['base_url'] + test_case['url']

            # 添加固定变量到共享变量中
            variables = test_case.get('variables',{})
            load_variables(variables,TestClass.shared_data)

            # 获取测试用例的参数
            name = test_case.get('name', None)
            method = test_case.get('method', None)
            params = test_case.get('params', None)

            # 打印日志
            LogUtils.info('【用例名称】：' + str(name))
            LogUtils.info('【接口请求地址】：' + url)
            LogUtils.info('【请求方式】：' + str(method))
            LogUtils.info('【接口请求参数】：' + str(params))

            # 替换params中的变量
            update_params(params,TestClass.shared_data)

            # # 获取查询参数
            query_params = test_case.get('query_params', None)

            # # 如果参数中包含token，替换为上一个接口返回的token值
            if query_params:
                if 'token' in query_params:
                    query_params['token'] = token
                    LogUtils.info('获取登录：' + token)
                    LogUtils.info(query_params)

            # 发送请求并获取响应
            response = RequestUtil().send_request(test_case['method'], url, params=params, query_params=query_params)
            LogUtils.info('【接口响应结果】：' + response.text.encode().decode("unicode_escape"))
            resp_obj = ResponseObject(response)

            # 获取公共参数
            extract = test_case.get('extract',{})
            resp_obj.load_extract(extract, TestClass.shared_data)

            # # 保存token值
            # if 'data' in response.json().keys():
            #     if 'token' in response.json()['data'].keys():
            #         token = response.json()['data']['token']

            assertions = test_case.get('assertions', [])
            LogUtils.info(f'【断言操作】:{assertions}')
            for assertion in assertions:
                for op in assertion:
                    items = assertion[op]
                    LogUtils.info(f"【执行断言操作】: {op} {items}")
                    update_items(items, TestClass.shared_data, resp_obj)
                    if op in AssertionMap:
                        assert_fn = AssertionMap[op]
                        assert_fn(items)
                    else:
                        LogUtils.error(f"【！！！未知断言操作】: {op}")
        finally:
            LogUtils.info('==============++++++++++> 测试用例执行结束 <++++++++++==============')
            LogUtils.info('{-------}')
            LogUtils.info('{-------}')
            LogUtils.info('{-------}')


    # yaml 断言操作
    # for assertion in test_case.get('assertions', []):
    #     if assertion['key'] == 'status_code':
    #         assert response.status_code == assertion['value']  # 验证状态码
    #         LogUtils.info(f"状态码断言: {assertion['value']}")  # 记录状态码断言结果
    #     elif assertion['key'] == 'status':
    #         response_data = response.json()
    #         assert response_data['status'] == assertion['value']  # 验证'status'字段的值
    #         LogUtils.info(f"状态断言: {assertion['value']}")  # 记录状态断言结果
    # LogUtils.info('========> 测试用例结束  <========')


if __name__ == '__main__':
    pytest.main()


